﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using EmaMetadata.Serializers;
using System.Data;

namespace EmaMetadata
{
    public static class Factory
    {
        #region Well-defined constants for convenience
        public static string ENTMERCH_NAMESPACE = "org.entmerch";

        public static int TITLE_COMPLETE_ID = 1;
        public static int TITLE_MEDIUM_ID = 2;
        public static int TITLE_BRIEF_ID = 3;
        public static int TITLE_SORTING_ID = 4;

        public static int DESC_SHORT_ID = 1;
        public static int DESC_LONG_ID = 2;
        public static int DESC_REQUIREMENTS_ID = 3;

        public static int MP_ONLINE_ID = 1001;
        public static int MP_LOCAL_ID = 1002;
        public static int MP_LAN_ID = 1003;
        public static int MP_COOPERATIVE_ID = 1004;
        public static int MP_NONE_ID = 1005;

        public static int PRICE_MSRP = 1001;

        public static string TABLE_COREGENRES = "Core Genres";
        public static string TABLE_PLATFORMS = "Platforms";
        public static string TABLE_PRIMARYPRODUCTLINE = "Primary Product Line";
        public static string TABLE_EDITIONTYPES = "Edition Types";
        public static string TABLE_DIGITALSTOREFRONTS = "Digital Storefronts";
        public static string TABLE_DIGITALSIZEMEASUREMENTTYPES = "Digital Size Measurement Types";
        public static string TABLE_IDENTIFIERTYPES = "Identifier Types";
        public static string TABLE_TITLETYPES = "Title Types";
        public static string TABLE_DESCRIPTIONTYPES = "Description Types";
        public static string TABLE_MULTIPLAYERTYPES = "Multi-Player Type";
        public static string TABLE_PRICINGTYPES = "Pricing Types";
        public static string TABLE_RATINGSAGENCIES = "Ratings Agencies";
        #endregion

        #region Loading and saving interfaces
        /// <summary>
        /// Load a batch of data from the specified filename
        /// </summary>
        /// <param name="filename"></param>
        public static EmaBatch LoadBatch(string filename)
        {
            XmlDocument d = new XmlDocument();
            d.Load(filename);
            return InternalLoad(d);
        }

        /// <summary>
        /// Load a batch of data from the specified string
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static EmaBatch LoadBatchFromString(string s)
        {
            XmlDocument d = new XmlDocument();
            d.LoadXml(s);
            return InternalLoad(d);
        }

        /// <summary>
        /// Internal loading function: figures out the correct serializer to use for this file
        /// </summary>
        /// <param name="sr"></param>
        /// <returns></returns>
        private static EmaBatch InternalLoad(XmlDocument doc)
        {
            // Figure out what version this file is!
            string ver = doc.DocumentElement.ValueOrDefault("RecordVersion");

            // Use the appropriate serializer
            BaseSerializer bs = BaseSerializer.Get(ver);
            return bs.Load(doc);
        }

        /// <summary>
        /// Save a batch to an XML file on disk
        /// </summary>
        /// <param name="batch">The batch to save</param>
        /// <param name="filename">The filename where the batch should be saved</param>
        /// <param name="RecordVersion">The record version to use.  If null, uses the latest version.</param>
        public static void SaveBatch(EmaBatch batch, string filename, string RecordVersion = null)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = Encoding.UTF8;
            using (XmlWriter xw = XmlWriter.Create(filename, settings)) {

                // Use the appropriate serializer
                BaseSerializer bs = BaseSerializer.Get(RecordVersion);
                bs.Save(xw, batch);
                xw.Close();
            }
        }

        /// <summary>
        /// Save a batch to a string
        /// </summary>
        /// <param name="batch">The batch to save</param>
        /// <param name="RecordVersion">The record version to use.  If null, uses the latest version.</param>
        /// <returns>The XML string for the batch</returns>
        public static string SaveBatchToString(EmaBatch batch, string RecordVersion = null)
        {
            using (var ms = new MemoryStream()) {
                var sw = new StreamWriter(ms);

                // Write the batch
                XmlWriter xw = XmlWriter.Create(sw);

                // Use the appropriate serializer
                BaseSerializer bs = BaseSerializer.Get(RecordVersion);
                bs.Save(xw, batch);
                xw.Close();

                // Now read back the string
                sw.Flush();
                ms.Position = 0;
                using (var sr = new StreamReader(ms)) {
                    return sr.ReadToEnd();
                }
            }
        }

        /// <summary>
        /// Save a batch to an XML file on disk using the latest record version available
        /// </summary>
        /// <param name="batch"></param>
        /// <param name="filename"></param>
        public static void SaveBatch(EmaBatch batch, string filename)
        {
            SaveBatch(batch, filename, null);
        }
        #endregion

        #region Convenience functions that operate on a single
        /// <summary>
        /// Convert a product to an XML string
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static string ProductToString(EmaProduct p)
        {
            using (var ms = new MemoryStream()) {
                var sw = new StreamWriter(ms);

                // Write the batch
                XmlWriter xw = XmlWriter.Create(sw);

                // Use the appropriate serializer
                BaseSerializer.Current.WriteProduct(xw, p);
                xw.Close();

                // Now read back the string
                sw.Flush();
                ms.Position = 0;
                using (var sr = new StreamReader(ms)) {
                    return sr.ReadToEnd();
                }
            }
        }

        /// <summary>
        /// Convert an XML string back into a product
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static EmaProduct StringToProduct(string s)
        {
            XmlDocument d = new XmlDocument();
            d.LoadXml(s);
            return BaseSerializer.Current.ReadProduct(d.DocumentElement);
        }
        #endregion

        #region Lookup code tables
        private static EmaLookupTable _builtin_dict = null;
        private static EmaLookupTable _user_dict = null;
        private static string _user_dict_filename = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "EMA\\userdefinedtables.xml");

        /// <summary>
        /// Add an item to the user defined dictionary
        /// </summary>
        /// <param name="tablename"></param>
        /// <param name="ns"></param>
        /// <param name="id"></param>
        /// <param name="factname"></param>
        /// <param name="notes"></param>
        public static LookupItem SetUserDefinedItem(string tablename, string ns, int id, string factname, string notes)
        {
            LookupItem li = _user_dict.AddItem(tablename, ns, id, factname, notes);
            _user_dict.Save(_user_dict_filename);
            return li;
        }

        /// <summary>
        /// Retrieve all lookup items matching the namespace and table name
        /// </summary>
        /// <param name="NS"></param>
        /// <param name="tablename"></param>
        /// <returns></returns>
        public static List<LookupItem> GetItems(string tablename)
        {
            // Have we loaded the document yet?
            if (_builtin_dict == null) {

                // Retrieve all data from the embedded resource document
                var d = new XmlDocument();
                d.LoadXml(Namespaces.EntMerch);

                // Populate the dictionary
                _builtin_dict = EmaLookupTable.Load(d);
            }

            // Have we read the user-defined document yet?
            if (_user_dict == null) {
                try {
                    var d = new XmlDocument();
                    d.Load(_user_dict_filename);

                    // Populate the dictionary
                    _user_dict = EmaLookupTable.Load(d);
                } catch (Exception ex) {
                    System.Diagnostics.Debug.WriteLine(ex.ToString());
                    _user_dict = new EmaLookupTable();
                }
            }

            // Produce the list of lookup items matching this definition
            List<LookupItem> result = new List<LookupItem>();
            string key = (tablename).ToUpper();
            result.AddRange(_builtin_dict.GetLookups(key));
            result.AddRange(_user_dict.GetLookups(key));
            return result;
        }

        #endregion

        #region ISO Codes
        private static Dictionary<string, string> _iso_countries = null;
        public static Dictionary<string, string> ISOCountries
        {
            get 
            {
                // Retrieve it first time through
                if (_iso_countries == null) {

                    // Read in the XML
                    _iso_countries = new Dictionary<string, string>();
                    var d = new XmlDocument();
                    d.LoadXml(Namespaces.ISO3166);
                    foreach (XmlNode n in d.DocumentElement.ChildNodes) {
                        _iso_countries[n.Attributes["code"].Value] = n.Attributes["name"].Value;
                    }
                }

                // Here's the dictionary
                return _iso_countries;
            }
        }

        private static Dictionary<string, string> _iso_currencies = null;
        public static Dictionary<string, string> ISOCurrencies
        {
            get
            {
                // Retrieve it first time through
                if (_iso_currencies == null) {

                    // Read in the XML
                    _iso_currencies = new Dictionary<string, string>();
                    var d = new XmlDocument();
                    d.LoadXml(Namespaces.ISO4217);
                    foreach (XmlNode n in d.DocumentElement.ChildNodes) {
                        _iso_currencies[n.Attributes["code"].Value] = n.Attributes["name"].Value;
                    }
                }

                // Here's the dictionary
                return _iso_currencies;
            }
        }
        #endregion
    }
}
